/// 
/// MaDAson - Toolkit for Model Driven Software Development
/// 
/// Copyright [2006] Marcus Lankenau
/// 
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///  
///     http://www.apache.org/licenses/LICENSE-2.0
///  
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
/// 

using System;
using System.Collections;
using System.Xml.Serialization;
using System.Text;
using System.Text.RegularExpressions;

namespace Madason.MetaModel
{
	/// <summary>
	/// Cardinality of an association-end
	/// </summary>
	public enum Cardinality
	{
		Undefined,
		One,
		Many
	}


	/// <summary>
	/// Association between classes
	/// </summary>
	public class Association : ModelElement
	{
		private string targetClassId;
		private Class @class;
		private Cardinality cardinality = Cardinality.One;
		private string otherEndID;
		private string cardinalityText;
		private bool isNavigable = true;
		protected string fkName;

		[XmlIgnore]
		public string FKName
		{
			set { fkName = value; }
		}

		
		[XmlAttribute("cardinality")]
		public string CardinalityText
		{
			get { return cardinalityText; }
			set { cardinalityText = value; }
		}
	

		[XmlAttribute("targetClass")]
		public string TargetClassId
		{
			get { return targetClassId; }
			set { targetClassId = value; }
		}

		[XmlAttribute("otherEnd")]
		public string OtherEndID
		{
			get { return otherEndID; }
			set { otherEndID = value; }
		}

		public bool IsNavigable
		{
			get { return isNavigable; }
			set { isNavigable = value; }
		}

		/// <summary>
		/// Target-Class of the association
		/// </summary>
		[XmlIgnore]
		public Class TargetClass
		{
			get
			{
				return Class.Model.FindClassById(TargetClassId);
			}
		}


		/// <summary>
		/// Source-Class of the association
		/// </summary>
		[XmlIgnore]
		public Class Class
		{
			get { return @class; }
			set { @class = value; }
		}

		
/*		[XmlIgnore]
		public string LowercaseGetter			
		{
			get 
			{			
				return "get" + NamePascalCase;
			}
		}

		[XmlIgnore]
		public string LowercaseSetter			
		{
			get 
			{			
				return "set" + NamePascalCase;
			}
		}	*/


		/// <summary>
		/// Other End of the Association
		/// </summary>
		[XmlIgnore]
		public Association OtherEnd
		{
			get
			{
				foreach (Association assoc in TargetClass.Associations)
				{
					if (assoc.Id == OtherEndID)
						return assoc;
				}
				return null;
			}
		}


		static Regex regexCardinalityRange = new Regex("^([*0-9]+)..([-*0-9]+)$");
		static Regex regexCardinality = new Regex("^([-*0-9]+)$");


		/// <summary>
		/// Cardinality of the Association
		/// </summary>
		[XmlIgnore]
		public Cardinality Cardinality
		{
			get
			{
				if (CardinalityText == null)
					return Cardinality.Undefined;
				Match match = regexCardinality.Match(CardinalityText);
				if (match.Success)
					return match.Groups[1].Value == "1" ? Cardinality.One : Cardinality.Many;
				match = regexCardinalityRange.Match(CardinalityText);
				if (match.Success)
					return match.Groups[2].Value == "1" ? Cardinality.One : Cardinality.Many;

				return Cardinality.Undefined;
			}
		}

		/// <summary>
		/// ture if the Association is n-n, otherwise false
		/// </summary>
		[XmlIgnore]
		public bool IsComplex
		{
			get
			{
				return Cardinality == Cardinality.Many && OtherEnd.Cardinality == Cardinality.Many;
			}
		}
			
	}


	#region Helper Types

	public delegate void AssociationAddDelegate(Association ns);

	public class AssociationList : ArrayList
	{
		public event AssociationAddDelegate AfterAssociationAdded;

		public void Add(Association at)
		{
			base.Add(at);
			if (AfterAssociationAdded != null)
				AfterAssociationAdded(at);
		}

		public new Association this[int i]
		{
			get
			{
				return base[i] as Association;
			}
			set
			{
				base[i] = value;
			}
		}
	}

	#endregion


}
